﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Seagull.Configuration;
using Seagull.DAL;

namespace Seagull.Backup
{
    /// <summary>
    /// 备份工作流程：
    ///     1. 查找本地压缩后的所有日期目录
    ///     2. 若备份目录下已有同名目录则删除（可能是未完成的备份）
    ///     3. 依次复制每一天的压缩后数据，每复制完一天的数据，将该日的压缩后数据删除
    /// </summary>
    public class BackupJob : MaintenanceJob
    {
        readonly PathHelper _compressPathHelper = new PathHelper(Config.Instance.PathConfig.CompressFolder);
        private readonly string _backupDirectory = PathHelper.GetBackupDirectory();

        public override void Start(CancellationToken cancellationToken)
        {
            var toBackupDirs = _compressPathHelper.DateDirectories.Select(t => t.Item1.FullName).ToArray();

            var backupedFolderCount = 0;
            foreach(var sourceFolder in toBackupDirs)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var targetFolder = sourceFolder.Replace(Config.Instance.PathConfig.CompressFolder, _backupDirectory);
                if(Directory.Exists(targetFolder))
                {
                    Directory.Delete(targetFolder, true);
                }
                try
                {
                    CopyDir(sourceFolder, targetFolder,
                        percentage => OnProgressChanged((int)((percentage + backupedFolderCount * 100.0) / toBackupDirs.Length)),
                        cancellationToken);
                    ++backupedFolderCount;
                }
                // 若复制目录中途取消，会抛出OperationCanceledException
                catch(OperationCanceledException)
                {
                    // 删除正在备份的目录
                    Directory.Delete(targetFolder, true);
                    throw;
                }
            }
        }

        private static void CopyDir(string sourcePath, string destinationPath,
            Action<int> onProgressChanged = null, 
            CancellationToken? cancellationToken = null)
        {
            onProgressChanged = onProgressChanged ?? (p => { });

            // 在开始复制前触发进度事件
            onProgressChanged(0);

            //Now Create all of the directories
            foreach(var dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
                Directory.CreateDirectory(dirPath.Replace(sourcePath, destinationPath));

            // 复制所有文件
            var files = Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories);
            var copiedFileCount = 0;
            foreach(var fileName in files)
            {
                if(cancellationToken.HasValue)
                {
                    cancellationToken.Value.ThrowIfCancellationRequested();
                }
                File.Copy(fileName, fileName.Replace(sourcePath, destinationPath), true);
                onProgressChanged((int)(++copiedFileCount * 100.0 / files.Length));
            }
        }
    }
}
